﻿Imports System.ComponentModel
Namespace Cues
	''' <summary>
	''' The types (besides the base) of cues available in the program.
	''' </summary>
	''' <remarks></remarks>
	Public Enum Types
		Audio
		Transport
		Stack
	End Enum

	''' <summary>
	''' The base class for all types of cues.
	''' </summary>
	''' <remarks></remarks>
	<Serializable> Public MustInherit Class CueBase
#Region "Implements"
		Implements INotifyPropertyChanged
		Implements IComparer
		Implements IComparable


#Region "Comparer"

		''' <summary>
		''' Compares one cue object to another.
		''' </summary>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare
			If TryCast(x, CueBase).Number >= 0 Then
				Return Equals(CType(x, CueBase).Number, CType(y, CueBase).Number)
			Else
				Return New CaseInsensitiveComparer().Compare(x.ToString, y.ToString)
			End If
		End Function

		''' <summary>
		''' Compares the current cue object to another.
		''' </summary>
		''' <param name="obj">The object to compare this cue with.</param>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Function CompareTo(obj As Object) As Integer Implements IComparable.CompareTo
			If Me.Number > CType(obj, CueBase).Number Then
				Return 1
			ElseIf Me.Number < CType(obj, CueBase).Number Then
				Return -1
			Else
				Return 0
			End If
		End Function


		Public Shared Operator >(obj1 As CueBase, obj2 As CueBase) As Boolean
			If obj1.Number > obj2.Number Then
				Return True
			Else
				Return False
			End If
		End Operator
		Public Shared Operator <(obj1 As CueBase, obj2 As CueBase) As Boolean
			If obj1.Number < obj2.Number Then
				Return True
			Else
				Return False
			End If
		End Operator



#End Region

#Region "Notification"
		<NonSerialized()> Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged

		Friend Overridable Sub RaisePropertyChanged(ByVal sProp As String)
			RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(sProp))
			If sProp = "CueList" Then
				RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("CueListCollection"))
			End If
			If sProp = "Number" AndAlso Me.GetType Is GetType(Cues.Stack) Then
				For Each c In CType(Me, Cues.Stack).CueList
					c.ParentCue = Me.Number
				Next
			End If
		End Sub
#End Region

#End Region

#Region "Events"

		''' <summary>
		''' Raised when the cue is stopped due to it being complete.
		''' </summary>
		''' <remarks></remarks>
		Public Event CueStopped(Cue As Cues.CueBase)

		''' <summary>
		''' Used by inheriting cue objects to raise the cue stopped event on the base object.
		''' </summary>
		''' <param name="Cue"></param>
		''' <param name="ManualStop"></param>
		''' <remarks></remarks>
		Protected Sub RaiseCueStopped(Cue As Cues.CueBase, Optional ManualStop As Boolean = False)
			RaiseEvent CueStopped(Cue)
			If TransportAction = CueActions.AdvanceAtEnd AndAlso Not ManualStop Then
				RaiseEvent StartTransportAction(Me, New EventArgs)
			ElseIf ManualStop AndAlso TransportActionTimer IsNot Nothing Then
				TransportActionTimer.Stop()
			End If
		End Sub

		''' <summary>
		''' Raised when the cue is ready to fire it's after action.
		''' </summary> 
		''' <param name="sender"></param>
		''' <param name="e"></param>
		''' <remarks></remarks>
		Public Event StartTransportAction(sender As Cues.CueBase, e As EventArgs)

		''' <summary>
		''' The timer that sets the delay for TransportActions.
		''' </summary>
		''' <remarks></remarks>
		<NonSerialized> Protected WithEvents TransportActionTimer As System.Timers.Timer

		''' <summary>
		''' Fires when the after action is ready to start.
		''' </summary>
		''' <param name="sender"></param>
		''' <param name="e"></param>
		''' <remarks></remarks>
		Private Sub FireTransportActionPause(sender As Object, e As System.Timers.ElapsedEventArgs) Handles TransportActionTimer.Elapsed
			RaiseEvent StartTransportAction(Me, New EventArgs)
			TransportActionTimer.Stop()
		End Sub


#End Region

#Region "Properties"

		Public ReadOnly Property IsActive As Boolean
			Get
				If (TransportActionTimer IsNot Nothing AndAlso TransportActionTimer.Enabled) OrElse Status = CueStatus.Active Then
					Return True
				Else
					Return False
				End If
			End Get
		End Property

		''' <summary>
		''' Represents the Number Property.
		''' </summary>
		''' <remarks></remarks>
		Private NumberProperty As Double

		''' <summary>
		''' Identifies the cue number the cue has within the show.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Property Number As Double
			Get
				Return NumberProperty
			End Get
			Set(value As Double)
				If value <> NumberProperty Then
					NumberProperty = value
					RaisePropertyChanged("Number")
					RaisePropertyChanged("FullNumber")
				End If
			End Set
		End Property

		''' <summary>
		''' If the ParentCue property is greater than 0 then this returns a composite of that property and the CueNumber property.
		''' Otherwise it returns the CueNumber property.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		<Xml.Serialization.XmlIgnore> Public Property FullNumber As String
			Get
				If ParentCue <> Nothing AndAlso ParentCue <> "-1" AndAlso ParentCue <> "0" AndAlso ParentCue <> "" Then
					Return ParentCue & ":" & Number
				Else
					Return Number
				End If
			End Get
			Private Set(value As String)
				'Does nothing
			End Set
		End Property

		Private ParentCueProperty As String = ""
		''' <summary>
		''' This should be set if the cue has a parent cue. Generally parent cues will only be StackCues.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Property ParentCue As String
			Get
				Return ParentCueProperty
			End Get
			Set(value As String)
				If ParentCueProperty <> value Then
					ParentCueProperty = value
					RaisePropertyChanged("ParentCue")
					RaisePropertyChanged("FullNumber")
				End If

			End Set
		End Property

		''' <summary>
		''' Represents the Description Property.
		''' </summary>
		''' <remarks></remarks>
		Private DescriptionProperty As String = ""
		''' <summary>
		''' An optional description that the user can create to describe the cue.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Property Description As String
			Get
				Return DescriptionProperty
			End Get
			Set(value As String)
				DescriptionProperty = value
				RaisePropertyChanged("Description")
			End Set
		End Property

		''' <summary>
		''' The current status of the cue.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Property Status As CueStatus
			Get
				Return StatusProperty
			End Get
			Set(value As CueStatus)
				If StatusProperty <> value Then
					StatusProperty = value
					If Me.GetType Is GetType(Cues.Stack) AndAlso CType(Me, Cues.Stack).CueList.Count > 0 Then
						If value = CueStatus.Cued Then
							If CType(Me, Cues.Stack).CueList.Item(0).Status <> CueStatus.Cued Then
								CType(Me, Cues.Stack).CueList.Item(0).Status = CueStatus.Cued
							End If
						ElseIf value = CueStatus.Standby Then
							For Each c In CType(Me, Cues.Stack).CueList
								c.Status = CueStatus.Standby
							Next
						End If
					End If
					If value = CueStatus.Active Then
						If TransportAction = CueActions.Pause AndAlso ActionDelay > 0 Then
							If TransportActionTimer Is Nothing Then TransportActionTimer = New Timers.Timer()
							TransportActionTimer.Interval = ActionDelay * 1000
							TransportActionTimer.AutoReset = False
							TransportActionTimer.Start()
						ElseIf TransportAction = CueActions.Pause AndAlso ActionDelay = 0 Then
							RaiseEvent StartTransportAction(Me, New EventArgs)
						End If
					End If
					RaisePropertyChanged("Status")
					If My.Application IsNot Nothing AndAlso My.Application.Show IsNot Nothing AndAlso My.Application.Show.ShowFile IsNot Nothing Then
						My.Application.Show.ShowFile.RaisePropertyChanged("ActiveCues")
					End If
				End If
			End Set
		End Property
		''' <summary>
		''' Represents the Status Property.
		''' </summary>
		''' <remarks></remarks>
		<NonSerialized> Private StatusProperty As CueStatus = CueStatus.Standby

		''' <summary>
		''' The action that occurs as soon as the cue is triggered.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Property TransportAction As CueActions
			Get
				Return TransportActionProperty
			End Get

			Set(ByVal value As CueActions)
				TransportActionProperty = value
				RaisePropertyChanged("TransportAction")
				RaisePropertyChanged("TransportActionIndex")
			End Set
		End Property

		''' <summary>
		''' Used in UI for determining selected TransportAction properly.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Property TransportActionIndex As Integer
			Get
				Return CInt(TransportActionProperty)
			End Get
			Set(value As Integer)
				If CInt(TransportActionProperty) <> value Then
					TransportActionProperty = value
					RaisePropertyChanged("TransportAction")
				End If
			End Set
		End Property

		''' <summary>
		''' Represents the TransportAction Property.
		''' </summary>
		''' <remarks></remarks>
		Private TransportActionProperty As CueActions = My.Settings.DefaultAfterAction

		''' <summary>
		''' When AfterAction is set to Pause this is the delay before triggering the next cue. Is in seconds.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Property ActionDelay As Double
			Get
				Return ActionDelayProperty
			End Get
			Set(value As Double)
				If value < 0.1 Then value = 0.1
				If ActionDelayProperty <> value Then
					ActionDelayProperty = value
					RaisePropertyChanged("ActionDelay")
				End If
			End Set
		End Property
		''' <summary>
		''' Represents the ActionDelay Property.
		''' </summary>
		''' <remarks></remarks>
		Private ActionDelayProperty As Double = My.Settings.DefaultDelay

		''' <summary>
		''' Represents the CueGUID Property
		''' </summary>
		''' <remarks></remarks>
		Private CueGUIDProperty As Guid = Guid.NewGuid()

		''' <summary>
		''' A unique Cue ID, guaranteed unique across all cue lists.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public ReadOnly Property CueGUID As Guid
			Get
				Return CueGUIDProperty
			End Get
		End Property

#End Region

#Region "Public Functions"

		''' <summary>
		''' Individual cues must override this to provide a relevent info string.
		''' </summary>
		''' <returns></returns>
		''' <remarks></remarks>
		Public MustOverride Overrides Function ToString() As String

		''' <summary>
		''' Used to prep the cue for processing.
		''' </summary>
		''' <remarks></remarks>
		Public MustOverride Sub Standby()


		''' <summary>
		''' Used to start the cue's functions.
		''' </summary>
		''' <remarks></remarks>
		Public MustOverride Sub Start()

		''' <summary>
		''' Used to stop the cue.
		''' </summary>
		''' <param name="Bool">Indicates whether the stop should include an optional boolean action.</param>
		''' <remarks></remarks>
		Public MustOverride Sub [Stop](Optional Bool As Boolean = True)

		''' <summary>
		''' If a cue is in standby this moves it to cued and vice-versa. If a cue is active this does nothing.
		''' </summary>
		''' <remarks></remarks>
		Public Sub BumpStatus()
			Try
				If Status = CueStatus.Standby Then
					Status = CueStatus.Cued
				ElseIf Status = CueStatus.Cued Then
					Status = CueStatus.Standby
				End If
			Catch ex As Exception
				My.Application.Show.Console.SendMessage("There was a problem changing the status of cue number: " & Number.ToString, ex)
			End Try
		End Sub

		''' <summary>
		''' Creates a new cue, optionally giving it a parent cue number.
		''' </summary>
		''' <param name="ParentCueNumber">The cue number of which parent it belongs to.</param>
		''' <remarks></remarks>
		Public Sub New(Optional ParentCueNumber As String = "-1")
			ParentCue = ParentCueNumber
		End Sub

#End Region

	End Class

	''' <summary>
	''' The possible statuses of a cue.
	''' </summary>
	''' <remarks></remarks>
	<Serializable> Public Enum CueStatus
		Cued
		Active
		Standby
	End Enum

	''' <summary>
	''' The possible actions a cue can take immediately after it's been fired.
	''' </summary>
	''' <remarks></remarks>
	<Serializable> Public Enum CueActions As Integer
		[Nothing]
		Pause
		[Next]
		AdvanceAtEnd
	End Enum

End Namespace